package com.path.planning.algorithm;

import com.path.planning.entity.RoutingNetwork;
import com.path.planning.entity.RoutingRequest;
import com.path.planning.entity.RoutingResult;
import com.path.planning.mapper.RoutingMapper;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.WKTReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * 路径规划引擎
 * 基于A*算法的多国家路网路径规划
 */
@Component
public class RoutingEngine {

    @Autowired
    private RoutingMapper routingMapper;

    private final WKTReader wktReader = new WKTReader();
    private final GeometryFactory geometryFactory = new GeometryFactory();

    /**
     * 查找路径
     */
    public RoutingResult findRoute(RoutingRequest request) {
        try {
            // 验证输入参数
            if (request == null || request.getStartPoint() == null || request.getEndPoint() == null) {
                return RoutingResult.error("起点和终点不能为空");
            }

            if (request.getCountryCode() == null || request.getCountryCode().trim().isEmpty()) {
                return RoutingResult.error("国家代码不能为空");
            }

            // 检查国家是否存在
            if (!routingMapper.countryExists(request.getCountryCode())) {
                return RoutingResult.error("不支持的国家代码: " + request.getCountryCode());
            }

            // 获取表名
            String tableName = routingMapper.getTableNameByCountryCode(request.getCountryCode());
            if (tableName == null) {
                return RoutingResult.error("无法获取国家对应的路网表");
            }

            // 查找起点和终点最近的路网段
            RoutingNetwork startNetwork = routingMapper.findNearest(
                tableName, 
                request.getStartPoint(), 
                request.getSearchRadius()
            );

            RoutingNetwork endNetwork = routingMapper.findNearest(
                tableName, 
                request.getEndPoint(), 
                request.getSearchRadius()
            );

            if (startNetwork == null) {
                return RoutingResult.error("在搜索半径内未找到起点附近的路网段");
            }

            if (endNetwork == null) {
                return RoutingResult.error("在搜索半径内未找到终点附近的路网段");
            }

            // 使用A*算法查找路径
            List<RoutingNetwork> path = findPathWithAStar(
                startNetwork, 
                endNetwork, 
                tableName,
                request.getMaxDistance()
            );

            if (path.isEmpty()) {
                return RoutingResult.error("未找到可行路径");
            }

            // 构建结果
            return buildRoutingResult(path, startNetwork, endNetwork);

        } catch (Exception e) {
            return RoutingResult.error("路径规划失败: " + e.getMessage());
        }
    }

    /**
     * 使用A*算法查找路径
     */
    private List<RoutingNetwork> findPathWithAStar(RoutingNetwork start, RoutingNetwork end, 
                                                  String tableName, Double maxDistance) {
        // 使用优先队列存储待探索的节点
        PriorityQueue<PathNode> openSet = new PriorityQueue<>(Comparator.comparingDouble(PathNode::getF));
        Map<Long, PathNode> allNodes = new HashMap<>();
        Set<Long> closedSet = new HashSet<>();

        // 初始化起点
        PathNode startNode = new PathNode(start.getId(), 0.0, calculateHeuristic(start, end), null);
        openSet.offer(startNode);
        allNodes.put(start.getId(), startNode);

        while (!openSet.isEmpty()) {
            PathNode current = openSet.poll();
            closedSet.add(current.getId());

            // 检查是否到达终点
            if (current.getId().equals(end.getId())) {
                return reconstructPath(current, allNodes, tableName);
            }

            // 检查距离限制
            if (current.getG() > maxDistance) {
                continue;
            }

            // 获取当前路网段
            RoutingNetwork currentNetwork = routingMapper.findById(tableName, current.getId());
            if (currentNetwork == null) {
                continue;
            }

            // 查找相邻的路网段
            List<RoutingNetwork> neighbors = findNeighbors(currentNetwork, tableName);
            
            for (RoutingNetwork neighbor : neighbors) {
                if (closedSet.contains(neighbor.getId())) {
                    continue;
                }

                double tentativeG = current.getG() + (neighbor.getLength() != null ? neighbor.getLength() : 0);
                
                PathNode existingNode = allNodes.get(neighbor.getId());
                if (existingNode == null) {
                    // 新节点
                    double h = calculateHeuristic(neighbor, end);
                    PathNode newNode = new PathNode(neighbor.getId(), tentativeG, h, current);
                    openSet.offer(newNode);
                    allNodes.put(neighbor.getId(), newNode);
                } else if (tentativeG < existingNode.getG()) {
                    // 找到更好的路径
                    existingNode.setG(tentativeG);
                    existingNode.setParent(current);
                    openSet.remove(existingNode);
                    openSet.offer(existingNode);
                }
            }
        }

        return new ArrayList<>();
    }

    /**
     * 查找相邻的路网段
     */
    private List<RoutingNetwork> findNeighbors(RoutingNetwork current, String tableName) {
        List<RoutingNetwork> neighbors = new ArrayList<>();

        // 查找以当前段终点为起点的路网段
        if (current.getTarget() != null) {
            neighbors.addAll(routingMapper.findBySource(tableName, current.getTarget()));
        }

        // 查找以当前段起点为终点的路网段
        if (current.getSource() != null) {
            neighbors.addAll(routingMapper.findByTarget(tableName, current.getSource()));
        }

        return neighbors;
    }

    /**
     * 计算启发式距离
     */
    private double calculateHeuristic(RoutingNetwork from, RoutingNetwork to) {
        if (from.getTargetPoint() != null && to.getSourcePoint() != null) {
            return calculateDistance(from.getTargetPoint(), to.getSourcePoint());
        }
        return 0.0;
    }

    /**
     * 计算两点间距离
     */
    private double calculateDistance(Geometry point1, Geometry point2) {
        if (point1 instanceof Point && point2 instanceof Point) {
            Point p1 = (Point) point1;
            Point p2 = (Point) point2;
            
            // 使用球面距离计算
            double lat1 = Math.toRadians(p1.getY());
            double lon1 = Math.toRadians(p1.getX());
            double lat2 = Math.toRadians(p2.getY());
            double lon2 = Math.toRadians(p2.getX());
            
            double dlat = lat2 - lat1;
            double dlon = lon2 - lon1;
            double a = Math.sin(dlat / 2) * Math.sin(dlat / 2) +
                      Math.cos(lat1) * Math.cos(lat2) *
                      Math.sin(dlon / 2) * Math.sin(dlon / 2);
            double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
            
            return 6371000 * c; // 返回米
        }
        return 0.0;
    }

    /**
     * 重构路径
     */
    private List<RoutingNetwork> reconstructPath(PathNode endNode, Map<Long, PathNode> allNodes, String tableName) {
        List<RoutingNetwork> path = new ArrayList<>();
        PathNode current = endNode;

        while (current != null) {
            RoutingNetwork network = routingMapper.findById(tableName, current.getId());
            if (network != null) {
                path.add(0, network); // 添加到开头
            }
            current = current.getParent();
        }

        return path;
    }

    /**
     * 构建路径规划结果
     */
    private RoutingResult buildRoutingResult(List<RoutingNetwork> path, RoutingNetwork start, RoutingNetwork end) {
        RoutingResult result = RoutingResult.success();

        // 计算总距离
        double totalDistance = path.stream()
            .mapToDouble(network -> network.getLength() != null ? network.getLength() : 0)
            .sum();

        result.setTotalDistance(totalDistance);
        result.setSegmentCount(path.size());
        result.setStartNodeId(start.getId());
        result.setEndNodeId(end.getId());

        // 构建路径段信息
        List<RoutingResult.RoutingSegment> segments = new ArrayList<>();
        for (RoutingNetwork network : path) {
            RoutingResult.RoutingSegment segment = new RoutingResult.RoutingSegment();
            segment.setId(network.getId());
            segment.setName(network.getName());
            segment.setGeom(network.getGeom());
            segment.setLength(network.getLength());
            segment.setSource(network.getSource());
            segment.setTarget(network.getTarget());
            segment.setSourcePoint(network.getSourcePoint());
            segment.setTargetPoint(network.getTargetPoint());
            segments.add(segment);
        }

        result.setSegments(segments);

        // 构建完整路径几何
        if (!path.isEmpty()) {
            result.setFullPath(buildFullPathGeometry(path));
        }

        return result;
    }

    /**
     * 构建完整路径几何
     */
    private Geometry buildFullPathGeometry(List<RoutingNetwork> path) {
        if (path.isEmpty()) {
            return null;
        }

        List<Coordinate> coordinates = new ArrayList<>();
        
        for (int i = 0; i < path.size(); i++) {
            RoutingNetwork network = path.get(i);
            if (network.getGeom() instanceof LineString) {
                LineString lineString = (LineString) network.getGeom();
                Coordinate[] coords = lineString.getCoordinates();
                
                if (i == 0) {
                    // 第一个段，添加所有坐标
                    coordinates.addAll(Arrays.asList(coords));
                } else {
                    // 后续段，跳过第一个坐标（避免重复）
                    for (int j = 1; j < coords.length; j++) {
                        coordinates.add(coords[j]);
                    }
                }
            }
        }

        if (coordinates.size() < 2) {
            return null;
        }

        return geometryFactory.createLineString(coordinates.toArray(new Coordinate[0]));
    }

    /**
     * 路径节点（用于A*算法）
     */
    private static class PathNode {
        private Long id;
        private double g; // 从起点到当前节点的实际距离
        private double h; // 从当前节点到终点的启发式距离
        private double f; // f = g + h
        private PathNode parent;

        public PathNode(Long id, double g, double h, PathNode parent) {
            this.id = id;
            this.g = g;
            this.h = h;
            this.f = g + h;
            this.parent = parent;
        }

        // Getter和Setter方法
        public Long getId() { return id; }
        public void setId(Long id) { this.id = id; }
        public double getG() { return g; }
        public void setG(double g) { this.g = g; this.f = this.g + this.h; }
        public double getH() { return h; }
        public void setH(double h) { this.h = h; this.f = this.g + this.h; }
        public double getF() { return f; }
        public PathNode getParent() { return parent; }
        public void setParent(PathNode parent) { this.parent = parent; }
    }
}
